// description:
//		6ginject包提供映射和各种方式(Map\MapTo\Set)的依赖注入器.
// author ...
// date 2014-05-15

package g6inject

import (
	"fmt"
	"reflect"
)

// Applicator 表示一个 映射到一个struct 的接口.
type Applicator interface {
	Apply(interface{}) error
}

// Invoker 表示一个 调用反射方法 的接口.
type Invoker interface {
	Invoke(interface{}) ([]reflect.Value, error)
}

// TypeMapper 表示一个 基于类型映射 的接口.
type TypeMapper interface {
	Map(interface{}) TypeMapper
	MapTo(interface{}, interface{}) TypeMapper
	Set(reflect.Type, reflect.Value) TypeMapper
	Get(reflect.Type) reflect.Value
}

// Injector 表示一个在结构体/方法参数中映射和依赖注入的接口.
// 继承Applicator、Invoker、TypeMapper接口
type Injector interface {
	Applicator
	Invoker
	TypeMapper

	// SetParent设置injector的parent.
	// 如果注入器在自己的map(类型-值映射集)中找不到Type对应的Value，
	// 继续上溯到父元素继续查找.
	SetParent(Injector)
}

// injector是g6inject包中唯一定义的struct，
// 所有的操作都是基于injector struct来进行的.
// 它有两个成员values和parent.
// 1).values用于保存注入的参数，
//	它是一个reflect.Type为键，reflect.Value为值的map.
// 2).parent表示父元素
type injector struct {
	values map[reflect.Type]reflect.Value
	parent Injector
}

/****************以下为具体实现******************/

// 取出引用指向的一个接口类型.
// 如果value不是一个指向接口的值将产生panic.
func InterfaceOf(value interface{}) reflect.Type {
	t := reflect.TypeOf(value) //取出value的实际类型

	// 如果t的类型为指针，使指针指向内存地址
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	// 不是接口产生panic，终止程序.
	// t.Kind()返回一种特定类型.
	if t.Kind() != reflect.Interface {
		panic("Called inject.InterfaceOf with a value that is not a pointer to an interface.")
	}

	return t
}

// 实例化并返回一个Injector.
func New() Injector {
	return &injector{values: make(map[reflect.Type]reflect.Value)}
}

// 映射依赖结构体中各个标记为'inject'的字段.
// 如果注入失败将会返回error.
func (inj *injector) Apply(val interface{}) error {
	v := reflect.ValueOf(val)

	// v.Kind()返回这个类型的特殊种类.
	// 如：struct,ptr,func等
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Struct {
		return nil // Should not panic here ?
	}

	//实际类型
	t := v.Type()

	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i) //获取
		structField := t.Field(i)

		//f.CanSet()字段可导出
		if f.CanSet() && structField.Tag == "inject" {
			ft := f.Type()
			v := inj.Get(ft)
			if !v.IsValid() {
				return fmt.Errorf("Not Found %v\n", ft)
			}

			f.Set(v)
		}
	}

	return nil
}

// Invoke试图调用一个interface{}提供的一个方法 .
// 提供的参数必须是Invoke方法的参数类型 .
// 这个方法返回一个反射过后的slice.
// 如果注入失败，返回一个error.
// 如果f不是一个方法也将产生panic.
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
	t := reflect.TypeOf(f)

	//如果f不是一个方法产生panic
	var in = make([]reflect.Value, t.NumIn())

	//遍历参数
	for i := 0; i < t.NumIn(); i++ {
		argType := t.In(i)
		val := inj.Get(argType)
		if !val.IsValid() {
			return nil, fmt.Errorf("Not Found %v \n", argType)
		}
		in[i] = val
	}

	return reflect.ValueOf(f).Call(in), nil
}

// 使用reflect.TypeOf映射动态类型val的具体值.
// 返回一个注册的TypeMapper.
func (inj *injector) Map(val interface{}) TypeMapper {
	inj.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
	return inj
}

// 同上，使用ptrkey映射val.
func (inj *injector) MapTo(val interface{}, ptrKey interface{}) TypeMapper {
	inj.values[InterfaceOf(ptrKey)] = reflect.ValueOf(val)
	return inj
}

// 设定反射的reflect.Type和reflect.Value.
// 返回已经映射注册的Typemapper.
func (inj *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
	inj.values[typ] = val
	return inj
}

// 根据reflect.Type取出Typemapper.
func (inj *injector) Get(typ reflect.Type) reflect.Value {
	val := inj.values[typ]
	if !val.IsValid() && inj.parent != nil {
		val = inj.parent.Get(typ)
	}

	return val
}

//设置当前对象的parent
func (inj *injector) SetParent(parent Injector) {
	inj.parent = parent
}
